import config from "../config"
import { lazyReportCache } from "../report";
import captureEvent from "../utils/captureEvent";
import { getPaths } from "../utils/getPath";

/**
 * 这个正则表达式用于匹配 JavaScript 错误栈中的堆栈跟踪信息中的单个条目，其中包含文件名、行号和列号等信息。
 * 具体来说，它匹配以下格式的文本：
 * at functionName (filename:lineNumber:columnNumber)
 * at filename:lineNumber:columnNumber
 * at http://example.com/filename:lineNumber:columnNumber
 * at https://example.com/filename:lineNumber:columnNumber
 */
const FULL_MATCH =
  /^\s*at (?:(.*?) ?\()?((?:file|https?|blob|chrome-extension|address|native|eval|webpack|<anonymous>|[-a-z]+:|.*bundle|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;

// 通过正则表达式解析一行的错误信息
// line 就是字符串, 按照一定格式写出来的错误信息
// 利用正则表达式从字符串 line 中提取出: filename, functionName, lineno, colno
function parseStackLine(line) { 
  const lineMatch = line.match(FULL_MATCH);
  if (!lineMatch) return {};

  return {
    filename: lineMatch[2] || '<anonymous>',
    functionName: lineMatch[1] || '',
    lineno: parseInt(lineMatch[3], 10) || undefined,
    colno: parseInt(lineMatch[4], 10) || undefined
  }
}

//解析错误堆栈
// stack 就是字符串, 按照一定格式写出来的错误信息
function parseStackFrames(stack) { 
  //如果没有stack直接返回[]
  if (!stack) return [];

  const frames = [];
  const lines = stack.split('\n').slice(1)

  for (const line of lines) { 
    const frame = parseStackLine(line); //分析一行的错误信息
    if (frame.filename) { 
      //放入到堆栈错误信息数组中
      frames.push(frame);
    }
  }
  return frames[0]
} 

export default function error() {
  onVanillaError()
  onVueError()
}

/**
 * 监听原生js报错
 */
function onVanillaError() {

  window.addEventListener('error', event => {
    const target = event.target
    const lastCaptureEvent = captureEvent()
    // 例如: 加载图片报错
    if (target && (target.src || target.href)) {
      const data = {
        errorType: 'resourceError',
        filename: target.src || target.href,
        tagName: target.tagName,
        message: `加载${target.tagName}失败`
      }
      console.log('资源错误', data)
      lazyReportCache('error', data)
    }
    // js运行报错
    else {
      const stack = event.error.stack
      const frame = parseStackFrames(stack)
      
      const data = {
        errorType: 'jsError',
        filename: frame.filename,
        functionName: frame.functionName,
        lineno: frame.lineno,
        colno: frame.colno,
        message: event.message,
        stack,
      }
      console.log('js error', data)
      lazyReportCache('error', data)
    }

  })
  // promise 报错
  window.addEventListener('unhandlerejection', event => {
    const reason = event.reason
    const stack = reason.stack
    const frame = parseStackFrames(stack)
    const lastCaptureEvent = captureEvent()

    const data = {
      errorType: 'promiseError',
      filename: frame.filename,
      functionName: frame.functionName,
      lineno: frame.lineno,
      colno: frame.colno,
      message: reason.message,
      stack,
    }

    console.log('promise 错误', data)
    lazyReportCache('error', data)
  })
}
/**
 * 监听vue报错
 */
function onVueError() {

  // vue 错误
  if (config.vue?.Vue) {
    config.vue.Vue.config.errorHandler = function (err, vm, info) {
      const stack = err.stack
      const frame = parseStackFrames(stack)
      const lastCaptureEvent = captureEvent() // 报错时, 最后一个触发的事件
      const path = getPaths(lastCaptureEvent)

      const data = {
        errorType: 'vueError',
        filename: frame.filename,
        functionName: frame.functionName,
        lineno: frame.lineno,
        colno: frame.colno,
        message: err.message,
        stack
      }

      console.log('vue 错误', data, path)
      lazyReportCache('error', data)
    }
  }
}